import os
import sys
import pandas as pd
from selenium_frame.lizi_frame import LiziDriver
from selenium_frame import logger
import json
from datetime import datetime
from jinja2 import Environment, FileSystemLoader
BASE_DIR = os.path.dirname(os.path.abspath(__file__))  # 指向 selenium_frame/
case_path = os.path.join(BASE_DIR, 'test_cases')     # selenium_frame/test_cases
results_path = os.path.join(BASE_DIR, 'results')    # selenium_frame/results


# 解析关键字，并执行用例的每一个步骤
def do_step_test(driver, index, step, action, xpath, input, expect_value, actual_value):
    # 用例执行结果
    result = "PASS"
    result_text = ""
    # --- 打开网页操作：open
    if action == "open":
        # 打开系统
        driver.navi_to_page(input)
        logger.info(f"step{index + 1}：{step} {input}")
    # --- 点击操作：click
    elif action == "click":
        result = driver.click(xpath)
        if result == "FAIL":
            result_text = f"step{index + 1}：{step} - 出错：{xpath}"
            return result, result_text
        else:
            logger.info(f"step{index + 1}：{step}")
    # --- 点击课件控件操作：clickVisibility
    elif action == "clickVisibility":
        result = driver.click_visibility(xpath)
        if result == "FAIL":
            result_text = f"step{index + 1}：{step} - 出错：{xpath}"
            return result, result_text
        else:
            logger.info(f"step{index + 1}：{step}")
    # --- 输入操作：sendkeys
    elif action == "sendkeys":
        result = driver.sendkeys(xpath, input)
        if result == "FAIL":
            result_text = f"step{index + 1}：{step} - 出错：{xpath}"
            return result, result_text
        else:
            logger.info(f"step{index + 1}：{step}：{input}")
    # --- 清空并输入操作：clearAndSendkeys
    elif action == "clearAndSendkeys":
        result = driver.clear_and_sendkeys(xpath, input)
        if result == "FAIL":
            result_text = f"step{index + 1}：{step} - 出错：{xpath}"
            return result, result_text
        else:
            logger.info(f"step{index + 1}：{step}：{input}")
    # --- 进入iframe操作：switchToIframe
    elif action == "switchToIframe":
        result = driver.switch_to_iframe(xpath)
        if result == "FAIL":
            result_text = f"step{index + 1}：{step} - 出错：{xpath}"
            return result, result_text
        else:
            logger.info(f"step{index + 1}：{step}：{input}")
    # --- 返回默认iframe操作：switchToDefaultIframe
    elif action == "switchToDefaultIframe":
        result = driver.switch_to_default()
    # --- 切换窗口操作：switchToWindow
    elif action == "switchToWindow":
        result = driver.switch_to_window(input)
        if result == "FAIL":
            result_text = f"step{index + 1}：{step} - 出错：{xpath}"
            return result, result_text
        else:
            logger.info(f"step{index + 1}：{step}")
    # --- 鼠标悬停操作：hover
    elif action == "hover":
        result = driver.hover(xpath)
        if result == "FAIL":
            result_text = f"step{index + 1}：{step} - 出错：{xpath}"
            return result, result_text
        else:
            logger.info(f"step{index + 1}：{step}")
    # --- 获取文本操作：getText
    elif action == "getText":
        text = driver.get_text(xpath)
        logger.info(f"step{index + 1}：{step}: {text}")
    # --- 获取title操作：getTitle
    elif action == "getTitle":
        title = driver.get_title()
        logger.info(f"step{index + 1}：{step}: {title}")
    # --- 处理用户操作：pageLoadTimeout
    elif action == "stopPageLoad":
        driver.stop_page_load()
        logger.info(f"step{index + 1}：{step}")
    # --- 关闭窗口操作：close
    elif action == "close":
        driver.close()
        logger.info(f"step{index + 1}：{step}")
    # --- 退出浏览器操作：quit
    elif action == "quit":
        driver.quit()
        logger.info(f"step{index + 1}：{step}")
    # --- 点击Enter操作：quit
    elif action == "clickEnter":
        driver.click_enter(xpath)
        logger.info(f"step{index + 1}：{step}")
    # --- 处理用户操作：sleep
    elif action == "sleep":
        driver.sleep(int(input))
        logger.info(f"step{index + 1}：{step}：{input}S")
    # 断言assertEqual assertUnequal assertContain
    elif action == "assertEqual" or action == "assertUnequal" or action == "assertContain":
        expect_value = str(expect_value)
        if actual_value == "title":
            actual_value = str(driver.get_title())
        elif actual_value == "text":
            actual_value = str(driver.get_text(xpath))
        else:
            logger.error("请输入正确的实际结果，title 或者 text")
        result = driver.assert_result(action, expect_value, actual_value)
        if result == "FAIL":
            result_text = f"step{index + 1}：{step} - 预期结果：{expect_value} 实际结果：{actual_value}"
            return result, result_text
        else:
            logger.info(f"step{index + 1}：{step}")
    # 返回当前步骤测试结果
    return result, result_text

# 执行用例
def do_case_test(case_path=case_path):
    # 测试前准备
    driver = LiziDriver("chrome") # 初始化chrome driver
    case_result = {}  # 测试结果：{"测试用例文件名": ["测试结果",  "错误信息"]}
    total_result = {"total": 0, "pass": 0, "fail": 0, "detail": case_result}  # 测试结果统计
    logger.info("测试用例文件列表：{}".format(sorted(os.listdir(case_path))))
    # 遍历文件夹中的所有文件
    for filename in sorted(os.listdir(case_path)):
        logger.info(f"测试用例文件：{filename}")
        # 检查xlsx
        if filename.endswith(".xlsx") and "~" not in filename:
            # 文件路径 + 文件名
            file = os.path.join(case_path, filename)
            # # 读取Excel文件，跳过第一行标题
            df = pd.read_excel(file, header=0)
            # 使用.iterrows()遍历每一行
            for index, row in df.iterrows():
                # 按步骤执行测试
                step_result, info = do_step_test(driver, index, row['步骤'], row['动作'], row['xpath'], row['输入'], row['预期结果'], row['实际结果'])
                # 如果测试步骤报错，不再执行其余步骤
                if step_result == "FAIL":
                    break
                else:
                    continue
            # 数据统计
            if step_result == "PASS":
                # 通过统计
                total_result["pass"] += 1
                total_result["total"] += 1
            else:
                # 失败统计
                total_result["fail"] += 1
                total_result["total"] += 1
            # 详情统计
            case_result[filename.split(".")[0]] = [step_result, info]
        else:
            continue
    logger.info("total_result：{}".format(total_result))
    return total_result


# 生成html报告
def generate_report(result):
    # 1. 用户输入转json
    if isinstance(result, str):
        data = json.loads(result)
    elif isinstance(result, dict):
        data = result

    # 2. 将 detail 转换成列表，方便模板循环
    rows = []
    for k, v in data['detail'].items():
        case_id, case_name = k.split('_', 1)  # 拆分编号与名称
        status, detail = v
        rows.append({
            'id': case_id,
            'name': case_name,
            'status': status,
            'detail': detail
        })

    # 3. 组装模板变量
    context = {
        'total': data['total'],
        'pass': data['pass'],
        'fail': data['fail'],
        'pass_rate': round(data['pass'] / data['total'] * 100, 2),
        'rows': rows
    }

    # 4. 渲染模板
    template_dir = os.path.dirname(os.path.abspath(__file__))
    env = Environment(loader=FileSystemLoader(template_dir))
    html = env.get_template('report_template.html').render(**context)

    # 5. 写出文件
    output_file = os.path.join(results_path, "UI自动化测试报告.html")
    # ⬇确保目录存在
    os.makedirs(results_path, exist_ok=True)
    with open(output_file, 'w', encoding='utf-8') as f:
        f.write(html)
    print(f'报告已生成：{os.path.abspath(output_file)}')
    # 给jenkins发送测试结果
    if result['fail'] != 0:
        sys.exit(1) # 失败
    else:
        pass    # 成功

if __name__ == '__main__':
    result = do_case_test()
    generate_report(result)

